/************************************************************************

								Heroes IV
					  Copyright 2002, The 3DO Company

	------------------------------------------------------------------
  						 adventure_enemy_marker.h

	$Header: $

	$NoKeywords: $

 ************************************************************************/

#if !defined( ADVENTURE_ENEMY_MARKER_H_INCLUDED )
#define ADVENTURE_ENEMY_MARKER_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <assert.h>
#include <list>
#include <vector>

#include "adventure_path.h"
#include "counted_ptr.h"
#include "path_search_type.h"
#include "uncopyable.h"

class	t_army;
struct	t_adv_map_point;
class	t_adventure_map;
class	t_adventure_path_finder_enemy_data_view;
class	t_creature_array;
enum	t_skill_mastery;

// ---------------------------------------------------------------
// data structure for adventure path finder
// ---------------------------------------------------------------
int const k_adv_path_sector_shift = 4;
int const k_adv_path_sector_size = 1 << k_adv_path_sector_shift;
int const k_adv_path_sector_mask = k_adv_path_sector_size - 1;
int const k_army_attack_radius = 2;
int const k_army_attack_array_size = 2 * k_army_attack_radius + 1;

class t_adv_path_sector : public t_counted_object
{
public:
	t_adv_path_sector();

	bool					enemies_are_marked() const;
	t_adventure_path_data&	get( int row, int column );
	void					set_enemies_marked( bool arg );
protected:
	bool					m_enemies_marked;
	t_adventure_path_data	m_data[k_adv_path_sector_size][k_adv_path_sector_size];
};

inline bool t_adv_path_sector::enemies_are_marked() const
{
	return m_enemies_marked;
}

inline t_adventure_path_data& t_adv_path_sector::get( int row, int column )
{
	return m_data[row][column];
}

inline void t_adv_path_sector::set_enemies_marked( bool arg )
{
	m_enemies_marked = arg;
}

// ---------------------------------------------------------------
// data structure for adventure path finder
// ---------------------------------------------------------------
class t_adventure_enemy_marker;

class t_adv_path_map 
{
public:
	t_adv_path_map( int size, int levels );

	void							clear();
	t_adventure_path_data&			get( t_adv_map_point const& point, bool mark_enemies = true );
	t_adventure_path_data const&	get( t_adv_map_point const& point, bool mark_enemies = true ) const;
	bool							point_exists( t_adv_map_point const& point ) const;
	void							set_parent( t_adventure_enemy_marker* parent );
protected:
	typedef t_counted_ptr<t_adv_path_sector> t_sector_ptr;

	int					calculate_data_index( t_adv_map_point const& point ) const;
	t_adv_path_sector*	get_sector( t_adv_map_point const& point, bool mark_enemies ) const;

	int									m_size;
	int									m_levels;
	t_adventure_enemy_marker*			m_parent;
	mutable std::vector<t_sector_ptr>	m_data;
};

inline int t_adv_path_map::calculate_data_index( t_adv_map_point const& point ) const
{
	int row = point.row >> k_adv_path_sector_shift;
	int column = point.column >> k_adv_path_sector_shift;

	assert( point.level >= 0 && point.level < m_levels );
	assert( row >= 0 && row < m_size );
	assert( column >= 0 && column < m_size );
	// compute index of data.  Result is [bridge][level][row][column],
	// in an array which is [2][m_levels][m_size][m_size]
	int index = (point.on_bridge * m_levels + point.level) * m_size + row;
	index = (index * m_size) + column;
	assert( index < (int)m_data.size() );
	
	return index;
}

inline t_adventure_path_data& t_adv_path_map::get( t_adv_map_point const& point, 
												   bool mark_enemies )
{
	int index = calculate_data_index( point );
	return get_sector( point, mark_enemies )->get( point.row & k_adv_path_sector_mask,
												   point.column & k_adv_path_sector_mask );
}

inline t_adventure_path_data const& t_adv_path_map::get( t_adv_map_point const& point, 
												         bool mark_enemies ) const
{
	int index = calculate_data_index( point );
	return get_sector( point, mark_enemies )->get( point.row & k_adv_path_sector_mask,
												   point.column & k_adv_path_sector_mask );
}

inline bool t_adv_path_map::point_exists( t_adv_map_point const& point ) const
{
	// compute index of data.  Result is [bridge][level][row][column],
	// in an array which is [2][m_levels][m_size][m_size]
	int index = calculate_data_index( point );
	return m_data[index].get() != 0;
}

inline void t_adv_path_map::set_parent( t_adventure_enemy_marker* parent )
{
	m_parent = parent;
}

// ---------------------------------------------------------------
// Base class of pathfinder; just marks enemies on the map
// ---------------------------------------------------------------
class t_adventure_enemy_marker : private t_uncopyable
{
	public:
		t_adventure_enemy_marker( t_adventure_map& map );
	
		void							clear();
		t_creature_array *				get_army();
		t_creature_array const*			get_army() const;
		t_adventure_path_data&			get_existing_data( t_adv_map_point const& point );
		t_adventure_path_data const&	get_existing_data( t_adv_map_point const& point ) const; 
		t_adventure_map *				get_map() const;
		t_path_search_type				get_path_type() const;
		void							initialize();
		void							set_army( t_creature_array* army );
		void							set_path_type( t_path_search_type path_type );

protected:
	friend class t_adv_path_map;
	typedef t_adventure_path_finder_enemy_data_view t_enemy_data_view;

	void find_blocked_directions( t_adventure_path_point const&	point,
								  t_creature_array const&		army,
								  bool							is_ship,
								  t_adv_map_point*				adjacent_points,
								  bool*							blocked,
								  bool*							valid_point );

	void mark_enemy( t_army const* enemy, t_enemy_data_view const & enemy_data_view,
					 t_skill_mastery stealth_level );
	void mark_enemies( t_adv_map_point const& top_left, int rows, int columns );

	t_creature_array*				m_army;
	t_adv_path_map					m_data;
	bool                            m_initialized;
	t_adventure_map&                m_map;
	t_path_search_type				m_path_type;

};

inline void t_adventure_enemy_marker::clear()
{
	m_initialized = false;
}

inline t_creature_array * t_adventure_enemy_marker::get_army()
{
	return m_army;
}

inline t_creature_array const* t_adventure_enemy_marker::get_army() const
{
	return m_army;
}

inline t_adventure_path_data& t_adventure_enemy_marker::get_existing_data( t_adv_map_point const& point )
{
	return m_data.get( point );	
}

inline t_adventure_path_data const& t_adventure_enemy_marker::get_existing_data( t_adv_map_point const& point ) const
{
	return m_data.get( point );	
}

inline t_adventure_map * t_adventure_enemy_marker::get_map() const
{
	return &m_map;
}

inline t_path_search_type t_adventure_enemy_marker::get_path_type() const
{
	return m_path_type;
}


#endif
